package org.reborn.math

import util.MathUtils

/**
 * A class representing a 3Dimensional line from start to end
 * Methods are also generic enough to be used as rays.
 * @param start the starting point of the line
 * @param end the ending point of the line
 */
case class Line3F(val start:VectorF,val end:VectorF){
  require(start.length == 3)
  require(end.length == 3)
  
  /**
   * Get the vector from start to end
   */
  lazy val vector = end - start
  
  /**
   * Get the length of the line
   */
  lazy val length = vector.magnitude
  
  /**
   * Get the direction the line from start to end
   */
  lazy val direction = vector / length
  
  
  
  /**
   * Test to determine if the line segment intersects the specified plane
   * @param plane the plane to test
   * @return true if it intersects, false otherwise
   */
  def intersects(plane:Plane3F):Boolean = {
	val intersectPoint = intersectsAt(plane)
	(intersectPoint >=0f && intersectPoint <=1f)
  }
  
  /**
   * Test to determine if the line segment intersects the specified polygon. 
   * @param poly the polygon to test
   * @return the distance along the line if it intersects, otherwise None
   */
  def intersectsAt(poly:Polygon3F):Option[Float] = {
	val intersectDist = intersectsAt(poly.plane)
	if(intersectDist <0f || intersectDist >1f)
	  return None
	  
	val pointOnPlane = pointOnLine(intersectDist)
	
	for(edge <- poly.edges){
	  val edgePlane = new Plane3F(edge.start,edge.direction cross poly.plane.normal)
	  if(edgePlane.dist(pointOnPlane) > 0 )
	    return None
	}
	
	return Some(intersectDist)
  }
  
  /**
   * Test to determine if the line segment intersects the specified sphere. 
   * @param poly the polygon to test
   * @return the distance along the line if it intersects, otherwise None
   */
  def intersectsSphereAt(center:VectorF,radius:Float):Seq[Float] = {
	val newStart = start - center
	
	val a = vector dot vector
	val b = (newStart * 2f) dot vector
	val c = (newStart dot newStart) - (radius * radius)
	  
	val det = (b * b) - (4f * a * c)
	if (det == 0){
	  val t = -b / (2*a)
	  return Array(t)
	}
	else if (det > 0){
	  val sqrtDet = math.sqrt(det).asInstanceOf[Float]
	  val denom = 2 * a
	  val t1 = (-b - sqrtDet) / denom
	  val t2 = (-b + sqrtDet) / denom
	  return Array(t1, t2)
	}
	else{
	  return new Array[Float](0) 
	}
  }
  
  def intersectsAt(plane:Plane3F):Float={
	MathUtils.intersectionDist(start,end,plane)
  }
  
  def pointOnLine(dist:Float):VectorF = {
	start + direction * length * dist  
  }
  
  def containsSamePoints(o:Line3F):Boolean = {
	(start == o.start && end == o.end) ||
	(start == o.end && end == o.start)
  }
  
  
}